// Sphere object

#include <math.h>
#include <iostream>

#include "../include/spheretexture.h"

#include "../my/include/Matrix.h"

#define pia 3.1415926

// Sphere defined as vertex (in world space) and radius

SphereTexture::SphereTexture(Vertex &psp, float pr, PPMImage *img) {
	sp = psp;
	r = pr;

	typex = "Sphere";
	typei = 0;
	image = img;
}

// Sphere intersection test

bool SphereTexture::intersect(Ray &input_ray, Hit *hit) {
	Ray ray=input_ray;
	Vector ro;
	//transform
	if (transformed != 0) {
		ray.D.multiply(invers_matrix);
		ray.P.multiply(invers_matrix);
	}
	ray.D.normalise();

	// offset ray by sphere position
	// equivalent to transforming ray into local sphere space

	ro.set(ray.P.x - sp.x, ray.P.y - sp.y, ray.P.z - sp.z);

	float a = ray.D.dot(ray.D);
	float b = 2.0 * ray.D.dot(ro);
	float c = ro.dot(ro) - r * r;

	float disc = b * b - 4 * a * c;

	if (disc < 0.0) {
		return false; // a negative value indicates no intersection.
	}

	float ds = sqrtf(disc);
	float q;

	if (b < 0.0) {
		q = (-b - ds) / 2.0;
	} else {
		q = (-b + ds) / 2.0;
	}

	float t0 = q / a;
	float t1 = c / q;

	if (t0 > t1) {
		float temp = t0;
		t0 = t1;
		t1 = temp;
	}

	if (t1 < 0.000000001) {
		return false;
	}

	// if an intersection has been found, record details in hit object

	hit->obj = this;




	if (t0 < 0.000000001) {
		hit->t = t1;

		hit->p.x = ray.P.x + t1 * ray.D.x;
		hit->p.y = ray.P.y + t1 * ray.D.y;
		hit->p.z = ray.P.z + t1 * ray.D.z;
		hit->p.w = 1.0;
		hit->n.x = hit->p.x - sp.x;
		hit->n.y = hit->p.y - sp.y;
		hit->n.z = hit->p.z - sp.z;

		if (transformed != 0) {
			hit->n.multiply(invers_matrix_T);
		}

		hit->n.normalise();



		hit->obj_mat = *this->obj_mat;
		// s = acos(z/r)/pia
		// t = acos(x/(r sin(pia s)))/2pia
		double localz = hit->p.z - sp.z;
		double localx = hit->p.x - sp.x;
		double u = acos(localz/r)/pia;
		double v = acos(localx/(r * sin(pia * u)))/(2* pia);

		if (u > 0.0 && v > 0.0 && u <= 1.0 && v <= 1.0) {
	//		std::cout << "u=" << u << "v=" << v << "ro1.x=" << ro1.x << std::endl;
			double imgw = image->x;
			double imgh = image->y;

			double imgx = imgw * u;
			double imgy = imgh * v;

			int imgintx = floor(imgx);
			int imginty = floor(imgy);

			int imgindex = imginty * imgw + imgintx;

			Colour co;
			double imgr = image->data[imgindex].red;
			double imgg = image->data[imgindex].green;
			double imgb = image->data[imgindex].blue;

			imgr = imgr / 255.0;
			imgg = imgg / 255.0;
			imgb = imgb / 255.0;
			co.set(imgr, imgg, imgb, 1);
			hit->obj_mat.ka = co;
		}


		hit->p.x = input_ray.P.x + t1 * input_ray.D.x; // p is position
		hit->p.y = input_ray.P.y + t1 * input_ray.D.y;
		hit->p.z = input_ray.P.z + t1 * input_ray.D.z;
		return true;
	}

	hit->t = t0;

	hit->p.x = ray.P.x + t0 * ray.D.x; // p is position
	hit->p.y = ray.P.y + t0 * ray.D.y;
	hit->p.z = ray.P.z + t0 * ray.D.z;
	hit->p.w = 1.0;
	hit->n.x = hit->p.x - sp.x; //n is normal
	hit->n.y = hit->p.y - sp.y;
	hit->n.z = hit->p.z - sp.z;


	if (transformed != 0) {
		hit->n.multiply(invers_matrix_T);
	}

	hit->n.normalise();

	hit->obj_mat = *this->obj_mat;
	// s = acos(z/r)/pia
	// t = acos(x/(r sin(pia s)))/2pia
	double localz = hit->p.z - sp.z;
	double localx = hit->p.x - sp.x;
	double u = acos(localz/r)/pia;
	double v = acos(localx/(r * sin(pia * u)))/(2* pia);

	if (u > 0.0 && v > 0.0 && u <= 1.0 && v <= 1.0) {
//		std::cout << "u=" << u << "v=" << v << "ro1.x=" << ro1.x << std::endl;
		double imgw = image->x;
		double imgh = image->y;

		double imgx = imgw * u;
		double imgy = imgh * v;

		int imgintx = floor(imgx);
		int imginty = floor(imgy);

		int imgindex = imginty * imgw + imgintx;

		Colour co;
		double imgr = image->data[imgindex].red;
		double imgg = image->data[imgindex].green;
		double imgb = image->data[imgindex].blue;

		imgr = imgr / 255.0;
		imgg = imgg / 255.0;
		imgb = imgb / 255.0;
		co.set(imgr, imgg, imgb, 1);
		hit->obj_mat.ka = co;
	}
	hit->p.x = input_ray.P.x + t0 * input_ray.D.x; // p is position
	hit->p.y = input_ray.P.y + t0 * input_ray.D.y;
	hit->p.z = input_ray.P.z + t0 * input_ray.D.z;

	return true;
}
